package com.derbysoft.synxisadapter.commons.utils;

import com.derbysoft.remote.core.validation.oval.OvalValidateSupport;
import com.derbysoft.remote.dswitch.generic.dto.PaymentCardDTO;
import com.derbysoft.synxisadapter.commons.dto.synxis.OtaMessage;
import com.derbysoft.synxisadapter.commons.dto.synxis.PaymentCard;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
import com.thoughtworks.xstream.converters.reflection.SerializableConverter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.xml.transform.StringResult;

import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.datatype.XMLGregorianCalendar;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public abstract class XMLUtils {
    private static Log logger = LogFactory.getLog(XMLUtils.class);
    private static XStream xStream = createXStream();

    private static Jaxb2Marshaller marshaller = createMarshaller();

    private static Pattern cardNumberPattern = Pattern.compile("(?<=CardNumber=\")[\\d|\\w]*(?=\")");

    private static Pattern expireDatePattern = Pattern.compile("(?<=ExpireDate=\")[\\d|\\w]*(?=\")");

    private static Jaxb2Marshaller createMarshaller() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setContextPath(OtaMessage.class.getPackage().getName());
        HashMap<String, Object> properties = new HashMap<String, Object>();
        properties.put(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        jaxb2Marshaller.setMarshallerProperties(properties);
        try {
            jaxb2Marshaller.afterPropertiesSet();
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        return jaxb2Marshaller;
    }

    private static XStream createXStream() {
        XStream xstream = new XStream();
        xstream.setMode(XStream.NO_REFERENCES);

        xstream.aliasPackage("", "org.apache.xerces.jaxp.datatype");
        xstream.aliasPackage("", "com.derbysoft.remote.dswitch.core");
        xstream.aliasPackage("", "com.derbysoft.lowcostadapter.dto");
        xstream.aliasPackage("", "com.derbysoft.remote.dswitch.hotel.dto");
        xstream.aliasPackage("", "com.derbysoft.remote.dswitch.generic.dto");
        xstream.omitField(OvalValidateSupport.class, "keepgoingValidate");
        xstream.omitField(OvalValidateSupport.class, "checkCircularReference");
        xstream.omitField(PaymentCard.class, "cardNumber");
        xstream.omitField(PaymentCard.class, "seriesCode");
        xstream.omitField(PaymentCard.class, "expireDate");
        xstream.omitField(PaymentCardDTO.class, "cardNumber");
        xstream.omitField(PaymentCardDTO.class, "seriesCode");
        xstream.omitField(PaymentCardDTO.class, "expireDate");

        xstream.registerConverter(new XMLGregorianCalendarConvert(), XStream.PRIORITY_VERY_HIGH);
        xstream.registerConverter(new SerializableConverter(xstream.getMapper(), xstream.getReflectionProvider(), null), XStream.PRIORITY_LOW + 2);
        xstream.registerConverter(new ReflectionConverter(xstream.getMapper(), xstream.getReflectionProvider()), XStream.PRIORITY_LOW + 1);
        return xstream;
    }

    public static String toXML(Object value) {
        try {
            if (value != null) {
                 XmlRootElement annotation = value.getClass().getAnnotation(XmlRootElement.class);
                if (annotation != null) {
                    return marshal(value, annotation.name());
                } else {
                    return xStream.toXML(value);
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return "";
    }

    private static String marshal(Object value, String name) {
        StringResult result = new StringResult();
        marshaller.marshal(value, result);
        if (name.contains("OTA_HotelRes")) {
            return hidePaymentCard(result.toString());
        }
        return result.toString();
    }

    private static String hidePaymentCard(String input) {
        return hideCardNumber(hideExpireDate(input));
    }

    private static String hideCardNumber(String input) {
        Matcher matcher = cardNumberPattern.matcher(input);
        return matcher.replaceAll("xxxxxxxxxxxxxxxx");
    }

    private static String hideExpireDate(String input) {
        Matcher matcher = expireDatePattern.matcher(input);
        return matcher.replaceAll("xxxx");
    }

    private static class XMLGregorianCalendarConvert extends AbstractSingleValueConverter {

        @Override
        public boolean canConvert(Class type) {
            return type.getGenericSuperclass() == XMLGregorianCalendar.class;
        }

        @Override
        public Object fromString(String str) {
            return null;
        }

        @Override
        public String toString(Object obj) {
            return obj.toString();
        }
    }
}
